package cn.com.cetccst.network;

import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

import androidx.annotation.NonNull;

import cn.com.cetccst.network.converter.JsonOrXmlConverterFactory;
import cn.com.cetccst.network.log.HttpLoggingInterceptor;
import cn.com.cetccst.network.log.RetrofitLogger;

import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;

public class RetrofitManager {

    private RetrofitManager() {
    }

    public static void setLogger(Logger logger) {
        RetrofitLogger.setLogger(logger);
    }

    public static class Builder {
        private static final X509TrustManager DEFAULT_TRUST_MANAGER = new TrustAllX509TrustManager();
        private static final long DEFAULT_TIMEOUT = 30L;
        private final WeakReference<Context> contextWeakReference;
        private final String baseUrl;
        private final OkHttpClient.Builder builder = new OkHttpClient.Builder();
        private final List<Interceptor> interceptors = new ArrayList<>();
        private X509TrustManager trustManager = DEFAULT_TRUST_MANAGER;
        private long timeout = DEFAULT_TIMEOUT;
        private HttpLoggingInterceptor.Level currentLevel = HttpLoggingInterceptor.Level.BODY;

        private final HostnameVerifier HOST_VERIFIER_CHECK = (hostname, session) -> {
            if (trustManager == DEFAULT_TRUST_MANAGER) {
                return true;
            } else {
                return hostname.equals(session.getPeerHost());
            }
        };

        public Builder(@NonNull Context context, @NonNull String baseUrl) {
            contextWeakReference = new WeakReference<>(context);
            this.baseUrl = baseUrl;
        }

        public Builder configSysSSL() {
            trustManager = systemDefaultTrustManager();
            return this;
        }

        public Builder configPrivateSSL(String assertFile) {
            InputStream[] certStream = null;
            try {
                certStream = new InputStream[]{contextWeakReference.get().getAssets().open("private_boot.pem")};
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            trustManager = prepareTrustManager(certStream);
            return this;
        }

        public Builder setTimeout(long timeout) {
            this.timeout = timeout;
            return this;
        }

        public Builder addInterceptor(Interceptor interceptor) {
            interceptors.add(interceptor);
            return this;
        }

        public Builder setLogLevel(HttpLoggingInterceptor.Level level) {
            currentLevel = level;
            return this;
        }

        public RetrofitClient build() {

            //证书配置
            builder.sslSocketFactory(systemDefaultSslSocketFactory(trustManager), trustManager);
            builder.hostnameVerifier(HOST_VERIFIER_CHECK);

            //超时配置
            builder.connectTimeout(timeout, TimeUnit.SECONDS)
                    .writeTimeout(timeout, TimeUnit.SECONDS)
                    .readTimeout(timeout, TimeUnit.SECONDS);

            //配置缓存（默认强制）
            configBuilderCache();

            //增加自定义拦截器
            for (Interceptor interceptor : interceptors) {
                builder.addInterceptor(interceptor);
            }

            //配置日志级别
            HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(message -> {
                RetrofitLogger.info("Rt= %s", message);
            });
            loggingInterceptor.setLevel(currentLevel);
            builder.addInterceptor(loggingInterceptor).build();

            OkHttpClient okHttpClient = builder.build();

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(okHttpClient)
                    .addConverterFactory(JsonOrXmlConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .build();

            return new RetrofitClient(retrofit, okHttpClient, loggingInterceptor);
        }

        private SSLSocketFactory systemDefaultSslSocketFactory(X509TrustManager trustManager) {
            try {
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{trustManager}, null);
                return sslContext.getSocketFactory();
            } catch (GeneralSecurityException e) {
                throw new AssertionError("No System TLS", e); // The system has no TLS. Just give up.
            }
        }

        private void configBuilderCache() {
            builder.addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    if (isNetworkConnected()) {
                        request = request.newBuilder()
                                .cacheControl(CacheControl.FORCE_NETWORK)
                                .build();
                    } else {
                        request = request.newBuilder()
                                .cacheControl(CacheControl.FORCE_CACHE)
                                .build();
                    }
                    return chain.proceed(request);
                }
            });

            File cacheDir = new File(contextWeakReference.get().getCacheDir(), "security_http_network_cache");
            int cacheSize = 10 * 1024 * 1024;
            Cache cache = new Cache(cacheDir, cacheSize);
            builder.cache(cache);
        }

        private boolean isNetworkConnected() {
            ConnectivityManager manager = (ConnectivityManager) contextWeakReference.get().getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo;
            if (manager != null) {
                networkInfo = manager.getActiveNetworkInfo();
                if (networkInfo != null && networkInfo.isConnected()) {
                    return true;
                }
            }
            return false;
        }

        private X509TrustManager prepareTrustManager(InputStream... certificates) {
            if (certificates == null || certificates.length <= 0) return null;

            TrustManager[] trustManagers = null;
            try {
                CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
                KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                keyStore.load(null);
                int index = 0;
                for (InputStream certificate : certificates) {
                    String certificateAlias = Integer.toString(index++);
                    keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
                }
                TrustManagerFactory trustManagerFactory;
                trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                trustManagerFactory.init(keyStore);
                trustManagers = trustManagerFactory.getTrustManagers();
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (trustManagers != null) {
                for (TrustManager trustManager : trustManagers) {
                    if (trustManager instanceof X509TrustManager) {
                        return (X509TrustManager) trustManager;
                    }
                }
            }
            return null;
        }

        /**
         * 如果只用默认忽略所有的X509TrustAllManager，可以使用，但是就能被设置代理抓包
         * <p>
         * 该函数提供只信任系统默认的TrustManager
         *
         * @return
         */
        private X509TrustManager systemDefaultTrustManager() {
            try {
                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                trustManagerFactory.init((KeyStore) null);
                TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
                if (trustManagers.length == 1 && trustManagers[0] instanceof X509TrustManager) {
                    return (X509TrustManager) trustManagers[0];
                } else {
                    throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
                }
            } catch (GeneralSecurityException var3) {
                throw new AssertionError("No System TLS", var3);
            }
        }
    }

    @SuppressLint("CustomX509TrustManager")
    private static class TrustAllX509TrustManager implements X509TrustManager {
        @SuppressLint("TrustAllX509TrustManager")
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @SuppressLint("TrustAllX509TrustManager")
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }
}
